<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>字符串分词高亮</title>
		<!-- Bootstrap CSS -->
		<link
			href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
			rel="stylesheet"
		/>
		<!-- Bootstrap JS -->
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
		<style>
			.container {
				margin-top: 2rem;
			}
			.search-text-normal {
				color: #333;
			}
			.search-text-active {
				color: #dc3545;
				font-weight: bold;
			}
		</style>
	</head>

	<body>
		<main class="container">
			<div class="card">
				<div class="card-header bg-primary text-white">
					<h5 class="card-title mb-0">文本搜索</h5>
				</div>
				<div class="card-body">
					<div class="mb-3">
						<input
							type="text"
							id="keyword-input"
							class="form-control"
							placeholder="请输入搜索关键词"
						/>
					</div>
					<div class="mb-3">
						<textarea
							id="data-input"
							class="form-control"
							rows="5"
							placeholder="请输入要搜索的文本，每行一条"
						></textarea>
					</div>
					<!-- 在搜索框下方添加复选框 -->
					<div class="mb-3 form-check">
						<input
							type="checkbox"
							class="form-check-input"
							id="continuous-check"
						/>
						<label class="form-check-label" for="continuous-check"
							>仅匹配完整关键词</label
						>
					</div>
					<div class="mb-3">
						<label class="form-label">命中率排序</label>
						<select id="sort-order" class="form-select">
							<option value="none">不排序</option>
							<option value="asc">升序（低到高）</option>
							<option value="desc">降序（高到低）</option>
						</select>
					</div>
					<button id="search-btn" class="btn btn-primary w-100">
						<i class="bi bi-search"></i> 搜索
					</button>
				</div>
			</div>

			<div id="search-results" class="mt-4">
				<!-- 搜索结果将在这里显示 -->
			</div>
		</main>
		<script>
			function tokenHighlightSearch(keyword, strList, opts = {}) {
				const { canContinuous = false, sort = "none" } = opts;
				// 空关键字处理：所有文本标记为 normal
				if (!keyword)
					return strList.map((str, id) => ({
						id,
						value: [
							{
								text: str,
								style: "normal",
							},
						],
					}));

				// 将关键词和文本都转换为小写进行匹配
				const lowKeyword = keyword.toLowerCase();

				let outList = strList.map((str, id) => {
					if (canContinuous) {
						// 连续匹配模式：只匹配完整关键词
						const segments = [];
						let lastIndex = 0;
						let lowStr = str.toLowerCase();
						let searchIndex = 0;

						while (
							(searchIndex = lowStr.indexOf(lowKeyword, lastIndex)) !== -1
						) {
							// 添加不匹配部分
							if (searchIndex > lastIndex) {
								segments.push({
									text: str.substring(lastIndex, searchIndex),
									style: "normal",
								});
							}
							// 添加匹配部分
							segments.push({
								text: str.substring(searchIndex, searchIndex + keyword.length),
								style: "active",
							});
							lastIndex = searchIndex + keyword.length;
						}
						// 添加剩余部分
						if (lastIndex < str.length) {
							segments.push({
								text: str.substring(lastIndex),
								style: "normal",
							});
						}

						return { id, value: segments };
					} else {
						// 单字符匹配模式：匹配所有关键词中的字符
						const keywordChars = Array.from(lowKeyword);
						const chars = Array.from(str);
						let value = [];
						let currentSegment = {
							text: chars[0],
							style: keywordChars.includes(chars[0].toLowerCase())
								? "active"
								: "normal",
						};

						for (let i = 1; i < chars.length; i++) {
							const currentStyle = keywordChars.includes(chars[i].toLowerCase())
								? "active"
								: "normal";

							if (currentStyle === currentSegment.style) {
								currentSegment.text += chars[i];
							} else {
								value.push(currentSegment);
								currentSegment = {
									text: chars[i],
									style: currentStyle,
								};
							}
						}
						value.push(currentSegment);

						return { id, value };
					}
				});

				// 计算命中率并排序
				if (sort !== "none" && keyword) {
					// 计算每个结果的命中率
					outList = outList.map(item => {
						// 计算高亮部分的总长度
						const highlightedLength = item.value
							.filter(segment => segment.style === "active")
							.reduce((sum, segment) => sum + segment.text.length, 0);
						
						// 计算命中率 (高亮文本长度 / 总文本长度)
						const totalLength = item.value.reduce((sum, segment) => sum + segment.text.length, 0);
						const hitRate = totalLength > 0 ? highlightedLength / totalLength : 0;
						
						return { ...item, hitRate };
					});
					
					// 根据命中率排序
					outList.sort((a, b) => {
						return sort === "asc" 
							? a.hitRate - b.hitRate 
							: b.hitRate - a.hitRate;
					});
				}

				return outList;
			}

			function renderSearchResults(results) {
				const container = document.getElementById("search-results");
				container.innerHTML = "";

				if (results.length === 0) {
					container.innerHTML = `
						<div class="alert alert-info" role="alert">
							没有找到匹配的结果
						</div>`;
					return;
				}

				const resultsList = document.createElement("div");
				resultsList.className = "list-group";

				results.forEach((item) => {
					const div = document.createElement("div");
					div.className = "list-group-item";

					item.value.forEach((segment) => {
						const span = document.createElement("span");
						span.textContent = segment.text;
						span.className = `search-text-${segment.style}`;
						div.appendChild(span);
					});

					resultsList.appendChild(div);
				});

				container.appendChild(resultsList);
			}

			// 获取DOM元素
			const keywordInput = document.getElementById("keyword-input");
			const dataInput = document.getElementById("data-input");
			const searchBtn = document.getElementById("search-btn");

			// 设置默认值
			const defaultData = [
				"我的手机和你的手机都是手和机器",
				"小米手机",
				"小米手机和红米手机",
				"手的作用和机器还是不能比的",
			];

			dataInput.value = defaultData.join("\n"); // 使用换行符替代逗号
			keywordInput.value = "手机";

			// 搜索事件
			searchBtn.addEventListener("click", () => {
				const keyword = keywordInput.value;
				const canContinuous =
					document.getElementById("continuous-check").checked;
				const sortOrder = document.getElementById("sort-order").value;
				const dataArray = dataInput.value
					.split("\n")
					.map((item) => item.trim())
					.filter(item => item.length > 0);

				const results = tokenHighlightSearch(keyword, dataArray, {
					canContinuous,
					sort: sortOrder
				});
				
				renderSearchResults(results);
			});

			// 初始搜索
			searchBtn.click();

			// 添加实时搜索功能
			keywordInput.addEventListener(
				"input",
				debounce(() => {
					searchBtn.click();
				}, 300)
			);

			// 防抖函数
			function debounce(func, wait) {
				let timeout;
				return function executedFunction(...args) {
					const later = () => {
						clearTimeout(timeout);
						func(...args);
					};
					clearTimeout(timeout);
					timeout = setTimeout(later, wait);
				};
			}
		</script>
	</body>
</html>